﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;
using ttbit.form.impl;
using ttbit.IGL;
using ttbit.math;

namespace ttbit.core
{

    public class Texture : IDisposable
    {
        public int TexID
        {
            get;
            internal set;
        }
        public ushort Width
        {
            get;
            internal set;
        }
        public ushort Height
        {
            get;
            internal set;
        }
        public TextureType Format
        {
            get;
            internal set;
        }
        public bool gray
        {
            get
            {
                return Format == IGL.TextureType.R_HalfFloat || Format == IGL.TextureType.R_Float || Format == IGL.TextureType.R_U8;
            }
        }

        //贴图格式不会经常变，早点搞定
        public Texture(ushort width, ushort height, IGL.TextureType format, byte[]? initdata = null)
        {
            this.Format = format;
            this.Width = width;
            this.Height = height;
            TexID = Graphics.GL.CreateTexture();
            Graphics.GL.BindTexture2D(TexID);
            Graphics.GL.TexImage2D(0, format, width, height, initdata);
        }

        public virtual void UploadData(ushort width, ushort height, IGL.TextureType format, byte[] data)
        {
            if (width != this.Width || height != this.Height)
                throw new Exception("error size");
            if (format != this.Format)
                throw new Exception("error format");

            Graphics.GL.BindTexture2D(TexID);
            Graphics.GL.TexImage2D(0, format, width, height, data);
        }
        public virtual void UploadData<T>(ushort width, ushort height, IGL.TextureType format, T[] data) where T : struct
        {
            if (width != this.Width || height != this.Height)
                throw new Exception("error size");
            if (format != this.Format)
                throw new Exception("error format");

            Graphics.GL.BindTexture2D(TexID);
            Graphics.GL.TexImage2D(0, format, width, height, data);
        }
        public virtual void UploadSubData(ushort umin, ushort vmin, ushort width, ushort height, IGL.TextureType format, byte[] data)
        {
            if (format != this.Format)
                throw new Exception("error format");

            Graphics.GL.BindTexture2D(this.TexID);
            Graphics.GL.TexSubImage2D(0, format, umin, vmin, width, height, data);
        }
        public virtual void Dispose()
        {
            Graphics.GL.DeleteTexture(TexID);
        }
        public virtual void ReadPixel<T>(T[] buf) where T : struct
        {
            int tfb = Graphics.GL.CreateFrameBuffer();
            Graphics.GL.BindFrameBuffer(tfb);
            Graphics.GL.FrameBufferTexture2D(FrameBufferSlot.ColorAttachment0, TexID, 0);
            Graphics.GL.ReadPixels(0, 0, Width, Height, Format, buf);
            Graphics.GL.BindFrameBuffer(0);
            Graphics.GL.DeleteFrameBuffer(tfb);
        }
        static public void PreMultiAlpha(byte[] data)
        {
            for (var i = 0; i < data.Length / 4; i++)
            {
                var r = data[i * 4 + 0] / 255.0f;
                var g = data[i * 4 + 1] / 255.0f;
                var b = data[i * 4 + 2] / 255.0f;
                var a = data[i * 4 + 3] / 255.0f;
                data[i * 4 + 0] = (byte)(r * a * 255.0f);
                data[i * 4 + 1] = (byte)(g * a * 255.0f);
                data[i * 4 + 2] = (byte)(b * a * 255.0f);
            }
        }
        static public void PreMultiAlpha(float[] data)
        {
            for (var i = 0; i < data.Length / 4; i++)
            {
                var r = data[i * 4 + 0];
                var g = data[i * 4 + 1];
                var b = data[i * 4 + 2];
                var a = data[i * 4 + 3];
                data[i * 4 + 0] = r * a;
                data[i * 4 + 1] = g * a;
                data[i * 4 + 2] = b * a;
            }
        }

    }

    public class FormTexture : ITexture
    {
        public Texture nativeTex;
        public byte[] pixelbuf;
        public TextureFormat format
        {
            get
            {
                if (nativeTex.Format == TextureType.RGBA_32)
                    return TextureFormat.RGBA32;
                else if (nativeTex.Format == TextureType.R_U8)
                    return TextureFormat.R8;
                else
                    throw new Exception("not support it.");
            }
        }
        public int width => nativeTex.Width;

        public int height => nativeTex.Height;


        public bool isStatic => pixelbuf == null;


        public uint ID => (uint)nativeTex.TexID;

        public void Dispose()
        {
            nativeTex?.Dispose();
            nativeTex = null;
        }
    }
    public class FormRenderTexture : IRenderTarget
    {
        public bool FlipY => false;

        public RenderTexture nativeTex;
        public ColorF ClearColor { get; set; }
        public Vector2 LookAt { get; set; }
        public float Scale { get; set; } = 1.0f;

        public TextureFormat format => TextureFormat.RGBA32;

        public int width => nativeTex.Width;

        public int height => nativeTex.Height;

        public bool isStatic => true;

        public uint ID => (uint)nativeTex.FBID;

        public void Dispose()
        {
            nativeTex?.Dispose();
            nativeTex = null;
        }

        public void Begin()
        {
            Graphics.GL.BindFrameBuffer(nativeTex.FBID);
            Graphics.GL.Viewport(0, 0, width, height);
            Graphics.GL.ClearColor(ClearColor.R, ClearColor.G, ClearColor.B, ClearColor.A);
            Graphics.GL.Clear(IGL.ClearFlag.ColorBufferBit);
        }

        public void End()
        {
            
        }
    }
    public class RenderTexture : Texture
    {
        public int FBID
        {
            get;
            private set;
        }



        public RenderTexture(ushort width, ushort height, IGL.TextureType format) : base(width, height, format, null)
        {
            FBID = Graphics.GL.CreateFrameBuffer();
        }
        public override void UploadData(ushort width, ushort height, TextureType format, byte[] data)
        {
            if (data != null)
                throw new Exception("not support.");
            this.Width = width;
            this.Height = height;
            Graphics.GL.BindTexture2D(TexID);
            Graphics.GL.TexImage2D(0, format, width, height, data);

        }
        public override void UploadData<T>(ushort width, ushort height, TextureType format, T[] data)
        {
            if (data != null)
                throw new Exception("not support.");
            this.Width = width;
            this.Height = height;
            Graphics.GL.BindTexture2D(TexID);
            Graphics.GL.TexImage2D(0, format, width, height, data);
        }
        public override void UploadSubData(ushort umin, ushort vmin, ushort width, ushort height, IGL.TextureType format, byte[] data)
        {
            throw new Exception("not support.");
        }
        public override void ReadPixel<T>(T[] buf) where T : struct
        {
            Graphics.GL.BindFrameBuffer(FBID);
            Graphics.GL.FrameBufferTexture2D(FrameBufferSlot.ColorAttachment0, TexID, 0);
            Graphics.GL.ReadPixels(0, 0, Width, Height, Format, buf);
            Graphics.GL.BindFrameBuffer(0);
        }
        public override void Dispose()
        {
            base.Dispose();
            Graphics.GL.DeleteFrameBuffer(FBID);
        }
        public void Active()
        {
            Graphics.GL.BindFrameBuffer(FBID);
            Graphics.GL.FrameBufferTexture2D(IGL.FrameBufferSlot.ColorAttachment0, TexID, 0);
            Graphics.GL.Viewport(0, 0, Width, Height);

        }
    }
}
